﻿using System;
using System.Collections.Generic;
using System.Text;
using Library.Gsm.AtCommands;
using System.Collections.Specialized;
using Library.Gsm.Wavecom;

namespace Library.Gsm {
  public abstract class BaseGsm : IGsm {
    public string ExeCommand(string command) {
      var cmd = Connection.CreateCommand();
      var textCommand = new TextCommand();
      textCommand.CreateCommandText(command);
      return cmd.Execute(textCommand).ToString();
    }

    public ConnectionType ConnectionType { get; set; }

    private SendSmsCommand _sendSmsCommand;
    protected virtual SendSmsCommand SendSmsCommand {
      get { return _sendSmsCommand ?? (_sendSmsCommand = new SendSmsCommand()); }
    }

    private ListSmsCommand _listSmsCommand;
    protected virtual ListSmsCommand ListSmsCommand {
      get { return _listSmsCommand ?? (_listSmsCommand = new ListSmsCommand()); }
    }

    private ReadSmsCommand _readSmsCommand;
    protected virtual ReadSmsCommand ReadSmsCommand {
      get { return _readSmsCommand ?? (_readSmsCommand = new ReadSmsCommand()); }
    }

    private DeleteSmsCommand _deleteSmsCommand;
    protected virtual DeleteSmsCommand DeleteSmsCommand {
      get { return _deleteSmsCommand ?? (_deleteSmsCommand = new DeleteSmsCommand()); }
    }

    private TestConnectionCommand _testConnectionCommand;
    protected virtual TestConnectionCommand TestConnectionCommand {
      get { return _testConnectionCommand ?? (_testConnectionCommand = new TestConnectionCommand()); }
    }

    private ManufacturerCommand _manufacturerCommand;
    protected virtual ManufacturerCommand ManufacturerCommand {
      get { return _manufacturerCommand ?? (_manufacturerCommand = new ManufacturerCommand()); }
    }

    private ModelCommand _modelCommand;
    protected virtual ModelCommand ModelCommand {
      get { return _modelCommand ?? (_modelCommand = new ModelCommand()); }
    }

    private HardwareVersionCommand _hardwareVersionCommand;
    protected virtual HardwareVersionCommand HardwareVersionCommand {
      get { return _hardwareVersionCommand ?? (_hardwareVersionCommand = new HardwareVersionCommand()); }
    }

    private DateOfProductCommand _dateOfProductCommand;
    protected virtual DateOfProductCommand DateOfProductCommand {
      get { return _dateOfProductCommand ?? (_dateOfProductCommand = new DateOfProductCommand()); }
    }

    private ImeiCommand _imeiCommand;
    protected virtual ImeiCommand ImeiCommand {
      get { return _imeiCommand ?? (_imeiCommand = new ImeiCommand()); }
    }

    private SerialNumberCommand _serialNumberCommand;
    protected virtual SerialNumberCommand SerialNumberCommand {
      get { return _serialNumberCommand ?? (_serialNumberCommand = new SerialNumberCommand()); }
    }

    private RevisionCommand _revisionCommand;
    protected virtual RevisionCommand RevisionCommand {
      get { return _revisionCommand ?? (_revisionCommand = new RevisionCommand()); }
    }




    protected abstract Sms ParseReadMsgResp(StringCollection response);

    protected abstract List<Sms> ParseListMsgResp(StringCollection response);

    protected virtual string[] ParseHeader(string header, int fromIndex) {
      StringBuilder lastParse = null;
      var result = new List<String>();
      var headerInfo = header.Substring(fromIndex).Trim();
      for (var i = 0; i < headerInfo.Length; i++) {
        var ci = headerInfo[i];
        switch (ci) {
          case '"':
            var strBuilder = new StringBuilder();
            for (int j = i + 1; j < headerInfo.Length; j++) {
              var cj = headerInfo[j];
              if (cj == '"') {
                lastParse = strBuilder;
                i = j;
                break;
              }

              strBuilder.Append(cj);

            }
            break;
          case ',':
            if (lastParse != null) {
              result.Add(lastParse.ToString());
            }
            lastParse = new StringBuilder();
            break;
          default:
            if (lastParse == null)
              lastParse = new StringBuilder();
            lastParse.Append(ci);
            break;
        }
        //if (lastParse == null) lastParse = new StringBuilder();
        //lastParse.Append(ci);


      }
      if (lastParse != null)
        result.Add(lastParse.ToString());
      return result.ToArray();
    }

    protected virtual string[] ParseHeader(string header) {
      int index = header.IndexOf(':');
      return ParseHeader(header, index);
    }

    protected static string ConvertSmsStatus(SmsStatus status) {
      switch (status) {
        case SmsStatus.Read:
          return "REC READ";
        case SmsStatus.UnRead:
          return "REC UNREAD";
        case SmsStatus.UnSent:
          return "STO UNSENT";
        case SmsStatus.Sent:
          return "STO SENT";
      }
      return String.Empty;
    }

    protected static SmsStatus ConvertSmsStatus(string upperStatus) {
      switch (upperStatus) {
        case "REC READ":
          return SmsStatus.Read;
        case "REC UNREAD":
          return SmsStatus.UnRead;
        case "STO UNSENT":
          return SmsStatus.UnSent;
        case "STO SENT":
          return SmsStatus.Sent;
      }
      throw new ResponseFormatException("Unknow status: " + upperStatus + ". Supported status: [REC READ], [REC UNREAD], [STO UNSENT], [STO SENT]");
    }

    private IGsmConnection _connection;
    protected IGsmConnection Connection {
      set { _connection = value; }
      get {
        if (_connection == null) throw new NullReferenceException("Call InitConnection first");
        return _connection;
      }
    }

    public virtual void SendMessage(string receiver, string message) {
      var command = Connection.CreateCommand();
      SendSmsCommand.CreateCommandText(receiver, message);

      var result = command.Execute(SendSmsCommand);
      if (result.FirstLine == null || !result.FirstLine.StartsWith("+CMGS:")) {
        throw new WavecomSendMsgException(result.ToString()) { CommandText = message };
      }
    }


    public List<Sms> ListMessages(SmsStatus status) {
      var command = Connection.CreateCommand();
      ListSmsCommand.CreateCommandText(status);
      return ParseListMsgResp(command.Execute(ListSmsCommand));
    }



    public Sms ReadMessage(int index) {
      var command = Connection.CreateCommand();
      ReadSmsCommand.CreateCommandText(index);
      return ParseReadMsgResp(command.Execute(ReadSmsCommand));
    }



    public void DeleteMessage(SmsDelFlag flag) {
      var command = Connection.CreateCommand();
      DeleteSmsCommand.CreateCommandText(1, (Int32)flag);
      command.Execute(DeleteSmsCommand);

    }

    public void DeleteMessage(int index) {
      var command = Connection.CreateCommand();
      DeleteSmsCommand.CreateCommandText(index, 0);
      command.Execute(DeleteSmsCommand);
    }


    public bool TestConnection(bool throwInnerException) {
      var command = Connection.CreateCommand();
      TestConnectionCommand.CreateCommandText();
      var result = command.Execute(TestConnectionCommand);

      try {
        return result.FirstLine == command.EndOk;
      } catch (Exception) {
        if (throwInnerException) throw;
        return false;
      }
    }


    public IGsmConnection GsmConnection {
      get { return _connection ?? (_connection = CreateConnect()); }
    }

    public IGsmIdentification Identification {
      get { return new GsmIdentification(this); }
    }

    public event NewMesssageReceivedEventHandler NewMessageReceived;

    protected void InvokeNewMessageReceived(NewMessageReceivedEventArgs eventargs) {
      NewMesssageReceivedEventHandler handler = NewMessageReceived;
      if (handler != null) handler(this, eventargs);
    }


    class GsmIdentification : IGsmIdentification {
      private readonly BaseGsm _baseGsm;
      public GsmIdentification(BaseGsm baseGsm) {
        _baseGsm = baseGsm;
        LoadValue();
      }

      private void LoadValue() {
        var warning = new StringBuilder();
        var command = _baseGsm.Connection.CreateCommand();
        try {
          _baseGsm.ManufacturerCommand.CreateCommandText();
          Manufacturer = command.Execute(_baseGsm.ManufacturerCommand).ToString();
        } catch (Exception ex) {
          warning.Append(ex);
        }
        try {
          _baseGsm.ModelCommand.CreateCommandText();
          Model = command.Execute(_baseGsm.ModelCommand).ToString();
        } catch (Exception ex) {
          warning.Append(ex);
        }
        try {
          _baseGsm.HardwareVersionCommand.CreateCommandText();
          HardwareVersion = command.Execute(_baseGsm.HardwareVersionCommand).ToString();
        } catch (Exception ex) {
          warning.Append(ex);
        }
        try {
          _baseGsm.DateOfProductCommand.CreateCommandText();
          DateOfProduct = command.Execute(_baseGsm.DateOfProductCommand).ToString();
        } catch (Exception ex) {
          warning.Append(ex);
        }
        try {
          _baseGsm.SerialNumberCommand.CreateCommandText();
          SerialNumber = command.Execute(_baseGsm.SerialNumberCommand).ToString();
        } catch (Exception ex) {
          warning.Append(ex);
        }
        try {
          _baseGsm.ImeiCommand.CreateCommandText();
          Imei = command.Execute(_baseGsm.ImeiCommand).ToString();
        } catch (Exception ex) {
          warning.Append(ex);
        }
        try {
          _baseGsm.RevisionCommand.CreateCommandText();
          Revision = command.Execute(_baseGsm.RevisionCommand).ToString();
        } catch (Exception ex) {
          warning.Append(ex);
        }
      }
      public string Manufacturer { get; private set; }

      public string Model {
        get;
        private set;
      }

      public string HardwareVersion {
        get;
        private set;
      }

      public string DateOfProduct {
        get;
        private set;
      }

      public string SerialNumber {
        get;
        private set;
      }

      public string Imei {
        get;
        private set;
      }

      public string Revision {
        get;
        private set;
      }
    }

    protected abstract IGsmConnection CreateConnect();

  }
}
